<template>
  <div class="image-container" :style="{ width: props.width, height: props.height }">
    <img 
      :src="isLoaded ? props.src : placeholder" 
      :alt="props.alt" 
      :style="props.imgStyle" 
      class="responsive-image"
      :class="{ 'image-loading': !isLoaded, 'image-loaded': isLoaded }"
      :loading="props.isLCP ? 'eager' : 'lazy'"
      :fetchpriority="props.isLCP ? 'high' : 'auto'"
      @load="onImageLoaded"
      @error="onImageError"
    >
    <div v-if="!isLoaded && !hasError" class="image-placeholder">
      <div class="loading-spinner"></div>
    </div>
    <div v-if="hasError" class="image-error">
      <span>Failed to load image</span>
    </div>
  </div>
</template>
 
<script setup lang="ts">
import { ref, onMounted } from 'vue'

const props = defineProps({
  src: {
    type: String,
    required: true
  },
  alt: {
    type: String,
    default: ''
  },
  width: {
    type: String,
    default: '100%'
  },
  height: {
    type: String,
    default: '100%'
  },
  imgStyle: {
    type: Object,
    default: () => ({
      'object-fit': 'fill',
      'aspect-ratio': '16 / 9',
      /**
       * object-fit的可能值:contain、cover、fill、none    ##根据自己需求做调节
       * object-fit的默认值是fill，这可能导致图像被挤压或拉伸
       * object-fit: contain时，图像将被黑边化或相应调整大小。
       * object-fit: cover时，图像将被剪裁以适应或相应地调整大小。
       * object-fit: fill时，图像将被相应地挤压、拉伸或调整大小。
       */
      /**
       *  例如，这里设置为16:9的宽高比
          aspect-ratio: 16 / 9
       */
    })
  },
  placeholder: {
    type: String,
    default: ''
  },
  isLCP: {
    type: Boolean,
    default: false
  }
})

const isLoaded = ref(false);
const hasError = ref(false);

const onImageLoaded = () => {
  isLoaded.value = true;
};

const onImageError = () => {
  hasError.value = true;
  isLoaded.value = true;
};

// 预加载图片
onMounted(() => {
  if (props.src) {
    const img = new Image();
    img.src = props.src;
    
    if (img.complete) {
      isLoaded.value = true;
    } else {
      img.onload = () => {
        isLoaded.value = true;
      };
      img.onerror = () => {
        hasError.value = true;
        isLoaded.value = true;
      };
    }
  }
});

</script>
 
<style scoped>
.image-container {
  width: 100%; /* 或者任何具体的宽度 */
  overflow: hidden; /* 防止内容溢出 */
  position: relative;
  background-color: #f5f5f5;
}

.responsive-image {
  width: 100%; /* 宽度自适应容器 */
  height: auto; /* 高度自动调整以保持宽高比 */
  object-fit: cover; /* 确保图片填满容器而不变形 */
  aspect-ratio: var(--aspect-ratio, 16 / 9); /* 使用 CSS 变量来设置宽高比 */
  transition: opacity 0.3s ease;
}

.image-loading {
  opacity: 0;
}

.image-loaded {
  opacity: 1;
}

.image-placeholder {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: #f5f5f5;
  z-index: 1;
}

.loading-spinner {
  width: 30px;
  height: 30px;
  border: 3px solid rgba(0, 0, 0, 0.1);
  border-radius: 50%;
  border-top-color: #000;
  animation: spin 1s ease-in-out infinite;
}

.image-error {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: #f5f5f5;
  color: #666;
  font-size: 14px;
}

@keyframes spin {
  to { transform: rotate(360deg); }
}
</style>